# app/services/tasks.py

import logging
import os
import re
from datetime import datetime, timedelta
import asyncio
import tempfile,aiohttp
import json
from datetime import datetime
import subprocess
from app.core.config import settings
from app.db.session import get_db_session
from app.models.models import Job, InternalJob
from app.services.oss_service import upload_file_to_oss, download_file, generate_signed_url, upload_image_to_oss
from app.services.huoshan_oss import upload_file
from app.utils.youshuai import ApiClient
from app.services.external_api import task_report
youshu_ai = ApiClient()

class TaskManager:

    async def get_video_duration(self, filename):
        try:
            # 使用 ffprobe 获取视频信息
            process = await asyncio.create_subprocess_exec(
                'ffprobe', '-v', 'error',
                '-show_entries', 'format=duration',
                '-of', 'json', filename,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            stdout, stderr = await process.communicate()
            if process.returncode == 0:
                output = stdout.decode()
                data = json.loads(output)
                duration = float(data['format']['duration'])
                return duration
            else:
                logging.error(f"ffprobe error: {stderr.decode()}")
                return 0.0
        except Exception as e:
            logging.error(f"获取视频时长时出错：{e}")
            return 0.0

    # 监控任务状态
    async def monitor_job_status(self, job_id: int,verify_taskId: int):
        """
        异步监控任务状态，直到任务完成并处理视频或音频。
        """
        logging.info(f"开始监控 job_id={job_id} 的状态")
        external_api_url = settings.EXTERNAL_API_URL.replace('/task/create', '/task/inspect')
        headers = {
            'Content-Type': settings.CONTENT_TYPE,
            'Authorization': settings.AUTHORIZATION
        }
        payload = {'job_id': job_id}
        taskId = verify_taskId

        max_retries = 360  # 最大轮询次数
        interval = 10     # 每次轮询的间隔（秒）

        for attempt in range(max_retries):
            await asyncio.sleep(interval)

            try:
                async with aiohttp.ClientSession() as session:
                    async with session.post(external_api_url, json=payload, headers=headers, timeout=30) as response:
                        response_data = await response.json()
                        logging.info(f"job_id={job_id} 轮询第 {attempt+1} 次，外部 API 响应：{response_data}")
            except Exception as e:
                logging.error(f"请求外部 API 时出错：{e}")
                continue

            if response_data.get('status') == 3:
                # 任务已完成，处理视频或音频
                # 从数据库中获取任务信息，获取 only_generate_audio 的值
                db = get_db_session()
                job = db.query(Job).filter(Job.job_id == job_id).first()
                if not job:
                    logging.error(f"未找到 job_id={job_id} 的记录")
                    db.close()
                    break

                only_generate_audio = job.only_generate_audio

                if only_generate_audio:
                    # 仅生成音频
                    # 提取 video_Url 并赋值给 audio_url，提取 duration 赋值给 video_duration
                    audio_url = response_data.get('video_Url')
                    video_duration = response_data.get('duration')
                    video_message = response_data.get('message')
                    # 由于外部 API 可能返回的是 video_Url 指向的音频文件
                    audio_url = response_data.get('audio_Url') or response_data.get('video_Url')  # 优先使用 audio_Url，如果没有则使用 video_Url
                    if not audio_url:
                        logging.error("外部 API 响应中缺少 audio_Url 或 video_Url")
                        db.close()
                        break
                    try:
                        # 上传音频到 OSS（使用 audio_url 提取文件名）
                        file_name = audio_url.split('/')[-1]  # 获取链接中的文件名
                        huoshan_oss_response = await upload_file(file_document = settings.OSS_VIDEO_TASK,url=audio_url, file_path = file_name)
                        logging.info(f"已上传音频到火山OSS：{huoshan_oss_response}")

                        # 更新数据库中的 audio_url 
                        oss_audio_url = huoshan_oss_response["oss_url"]
                        job.audio_url = oss_audio_url

                        # 更新数据库中的视频/音频时长
                        job.video_duration = video_duration

                        # 更新错误码
                        job.message = video_message

                        # 设置 video_time 为当前时间戳
                        video_time = int(datetime.utcnow().timestamp())

                        job.video_time = video_time
                        new_timestamp = video_time + 30 * 24 * 60 * 60  # 30天的秒数
                        job.expire_time = new_timestamp

                        db.commit()
                        logging.info(f"已更新数据库，job_id={job_id} 的 audio_url 和,video_duration, message,video_time")

                        # 生成回调接口
                        # youshu_ai.build_status(task_id=taskId, status="success", remake="音频任务已完成")

                        db.close()
                        break  # 任务已完成，结束轮询
                    except Exception as e:
                        logging.error(f"处理音频时出错：{e}")
                        db.close()
                        break
                else:
                    # 处理视频
                    video_url = response_data.get('video_Url')
                    video_duration = response_data.get('duration')
                    video_message = response_data.get('message')
                    if not video_url:
                        logging.error("外部 API 响应中缺少 video_Url")
                        db.close()
                        break

                    try:

                        file_name = video_url.split('/')[-1]  # 获取链接中的文件名
                        match = re.match(r"([a-zA-Z0-9-]+)\.mp4", file_name)
                        file_name = match.group(1)
                        print("链接文件名file_name: ",file_name)
                        huoshan_oss_response = await upload_file(file_document = settings.OSS_VIDEO_TASK,url=video_url, file_path = file_name)
                        logging.info(f"已上传音频到火山OSS：{huoshan_oss_response}")

                        # 更新数据库中的 video_url 
                        oss_video_url = huoshan_oss_response["oss_url"]
                        job.video_url = oss_video_url

                        # 设置 video_time 为当前时间戳
                        video_time = int(datetime.utcnow().timestamp())

                        job.video_time = video_time
                        new_timestamp = video_time + 30 * 24 * 60 * 60  # 30天的秒数
                        job.expire_time = new_timestamp
                        # 设置 video_duration
                        job.video_duration = video_duration

                        # 更新错误码
                        job.message = video_message

                        db.commit()
                        report_code = await task_report(verify_taskId=verify_taskId,job_id=job_id)
                        print(f"已上报{report_code}")
                        logging.info(f"已上报{report_code}")
                        logging.info(f"已更新数据库，job_id={job_id} 的 video_url、video_time 和 video_duration")
                        
                        # 生成回调接口
                        youshu_ai.build_status(task_id=taskId, status="success", remake="视频任务已完成")

                        db.close()
                        break  # 任务已完成，结束轮询
                    except Exception as e:
                        logging.error(f"处理视频时出错：{e}")
                        db.close()
                        break
            elif response_data.get('status') == 4:
                # 任务失败
                message = response_data.get('message')
                code = response_data.get('code')
                request_id = response_data.get('request_id')
                
                # 更新数据库中的任务状态和错误信息
                db = get_db_session()
                job = db.query(Job).filter(Job.job_id == job_id).first()
                if job:
                    job.message = message
                    job.error_code = code
                    job.request_id = request_id
                    db.commit()
                    logging.info(f"已更新数据库，job_id={job_id} 的失败信息: message={message}, code={code}, request_id={request_id}")
                db.close()
                
                logging.error(f"任务 job_id={job_id} 处理失败")
                youshu_ai.build_status(task_id=taskId, status="fail", remake=f"任务生成失败,{message}")
                break
            else:
                # 任务未完成，继续轮询
                continue
        else:
            # 超过最大轮询次数
            logging.error(f"任务 job_id={job_id} 在指定的时间内未完成")
            
    async def monitor_internal_job_status(self, job_id: int, verify_taskId: int):
        """
        异步监控内部任务状态，直到任务完成并处理视频或音频。
        """
        logging.info(f"开始监控内部任务 job_id={job_id} 的状态")
        external_api_url = settings.EXTERNAL_API_URL.replace('/task/create', '/task/inspect')
        headers = {
            'Content-Type': settings.CONTENT_TYPE,
            'Authorization': settings.AUTHORIZATION
        }
        payload = {'job_id': job_id}
        taskId = verify_taskId

        max_retries = 360  # 最大轮询次数
        interval = 10     # 每次轮询的间隔（秒）

        for attempt in range(max_retries):
            await asyncio.sleep(interval)

            try:
                async with aiohttp.ClientSession() as session:
                    async with session.post(external_api_url, json=payload, headers=headers, timeout=30) as response:
                        try:
                            response_data = await response.json()
                        except:
                            # 如果无法解析为JSON，获取原始文本
                            response_text = await response.text()
                            logging.error(f"内部任务 job_id={job_id} 轮询第 {attempt+1} 次，外部 API 响应非JSON格式：{response_text}")
                            continue
                        logging.info(f"内部任务 job_id={job_id} 轮询第 {attempt+1} 次，外部 API 响应：{response_data}")
            except Exception as e:
                logging.error(f"请求外部 API 时出错：{e}")
                continue

            # 确保response_data是字典类型
            if not isinstance(response_data, dict):
                logging.error(f"内部任务 job_id={job_id} 外部API响应格式错误，期望字典但得到：{type(response_data)}")
                continue

            if response_data.get('status') == 3:
                # 任务已完成，处理视频或音频
                # 从数据库中获取任务信息，获取 only_generate_audio 的值
                db = get_db_session()
                job = db.query(InternalJob).filter(InternalJob.job_id == job_id).first()
                if not job:
                    logging.error(f"未找到内部任务 job_id={job_id} 的记录")
                    db.close()
                    break

                only_generate_audio = job.only_generate_audio

                if only_generate_audio:
                    # 仅生成音频
                    # 提取 video_Url 并赋值给 audio_url，提取 duration 赋值给 video_duration
                    audio_url = response_data.get('video_Url')
                    video_duration = response_data.get('duration')
                    video_message = response_data.get('message')
                    # 由于外部 API 可能返回的是 video_Url 指向的音频文件
                    audio_url = response_data.get('audio_Url') or response_data.get('video_Url')  # 优先使用 audio_Url，如果没有则使用 video_Url
                    if not audio_url:
                        logging.error("外部 API 响应中缺少 audio_Url 或 video_Url")
                        db.close()
                        break
                    try:
                        # 上传音频到 OSS（使用 audio_url 提取文件名）
                        file_name = audio_url.split('/')[-1]  # 获取链接中的文件名
                        huoshan_oss_response = await upload_file(file_document = settings.OSS_VIDEO_TASK, url=audio_url, file_path = file_name)
                        logging.info(f"已上传内部任务音频到火山OSS：{huoshan_oss_response}")

                        # 更新数据库中的 audio_url 
                        oss_audio_url = huoshan_oss_response["oss_url"]
                        job.audio_url = oss_audio_url

                        # 更新数据库中的视频/音频时长
                        job.video_duration = video_duration

                        # 更新错误码
                        job.message = video_message

                        # 设置 video_time 为当前时间戳
                        video_time = int(datetime.utcnow().timestamp())

                        job.video_time = video_time
                        new_timestamp = video_time + 30 * 24 * 60 * 60  # 30天的秒数
                        job.expire_time = new_timestamp

                        db.commit()
                        logging.info(f"已更新内部数据库，job_id={job_id} 的 audio_url 和,video_duration, message,video_time")

                        db.close()
                        break  # 任务已完成，结束轮询
                    except Exception as e:
                        logging.error(f"处理内部任务音频时出错：{e}")
                        db.close()
                        break
                else:
                    # 处理视频
                    video_url = response_data.get('video_Url')
                    video_duration = response_data.get('duration')
                    video_message = response_data.get('message')
                    if not video_url:
                        logging.error("外部 API 响应中缺少 video_Url")
                        db.close()
                        break

                    try:
                        file_name = video_url.split('/')[-1]  # 获取链接中的文件名
                        match = re.match(r"([a-zA-Z0-9-]+)\.mp4", file_name)
                        file_name = match.group(1)
                        print("链接文件名file_name: ", file_name)
                        huoshan_oss_response = await upload_file(file_document = settings.OSS_VIDEO_TASK, url=video_url, file_path = file_name)
                        logging.info(f"已上传内部任务视频到火山OSS：{huoshan_oss_response}")

                        # 更新数据库中的 video_url 
                        oss_video_url = huoshan_oss_response["oss_url"]
                        job.video_url = oss_video_url

                        # 设置 video_time 为当前时间戳
                        video_time = int(datetime.utcnow().timestamp())

                        job.video_time = video_time
                        new_timestamp = video_time + 30 * 24 * 60 * 60  # 30天的秒数
                        job.expire_time = new_timestamp
                        # 设置 video_duration
                        job.video_duration = video_duration

                        # 更新错误码
                        job.message = video_message

                        db.commit()
                        # 内部任务不需要上报
                        # report_code = await task_report(verify_taskId=verify_taskId, job_id=job_id)
                        # print(f"已上报{report_code}")
                        # logging.info(f"已上报{report_code}")
                        logging.info(f"已更新内部数据库，job_id={job_id} 的 video_url、video_time 和 video_duration")

                        db.close()
                        break  # 任务已完成，结束轮询
                    except Exception as e:
                        logging.error(f"处理内部任务视频时出错：{e}")
                        db.close()
                        break
            elif response_data.get('status') == 4:
                # 任务失败
                message = response_data.get('message')
                code = response_data.get('code')
                request_id = response_data.get('request_id')
                
                # 更新内部数据库中的任务状态和错误信息
                db = get_db_session()
                job = db.query(InternalJob).filter(InternalJob.job_id == job_id).first()
                if job:
                    job.message = message
                    job.error_code = code
                    job.request_id = request_id
                    db.commit()
                    logging.info(f"已更新内部数据库，job_id={job_id} 的失败信息: message={message}, code={code}, request_id={request_id}")
                db.close()
                
                logging.error(f"内部任务 job_id={job_id} 处理失败")
                break
            else:
                # 任务未完成，继续轮询
                continue
        else:
            # 超过最大轮询次数
            logging.error(f"内部任务 job_id={job_id} 在指定的时间内未完成")
